#include <iostream>
using namespace std;

#include <Glut/glut.h>

#include "TriVertex.h"
#include "TriEdge.h"
#include "TriFace.h"
#include "TriMesh.h"
#include "TriCirculator.h"


//////////////////////////////////////////////////////////////////////////////
// TriVertex functions
//////////////////////////////////////////////////////////////////////////////

TriVertex::~TriVertex(void)
{

}

void TriVertex::Initialize(void)
{
   //Basic mesh
   marked=false;
   feature=false;
   corner=false;
   id=-1;
   coordsOld=coords=coords0=Vector3(0.0, 0.0, 0.0);
   edge=NULL;
   normal=Vector3(1.0, 0.0, 0.0);
   valence=-1;
   boundary=false;
   owner = NULL;
   collTime = 1.0;
}

bool TriVertex::IsFixed(void) const
{
   return true;
}

void TriVertex::UpdatePosition(void)
{

}

TriFace* TriVertex::AdjFace(TriVertex* v) const
{
   return AdjEdge(v)->AdjFace();
}

TriEdge* TriVertex::AdjEdge(TriVertex* v) const
{
   CWVertexCirculator loop(this);
   for(loop.Start(); !loop.IsDone(); loop.Advance())
   {
      if(loop.Get()->Head()==v)
      {
         return loop.Get();
      }
   }
//#ifdef _DEBUG
//   cerr << "There is no adjacent edge from vertex (id) " << Id() << " to vertex (id) " << v->Id() << endl;
//#endif
   return NULL;
}

bool TriVertex::IsAdjacentTo(const TriFace* f) const
{
   CWVertexCirculator loop(this);
   for(loop.Start(); !loop.IsDone(); loop.Advance())
   {
      TriEdge *e = loop.Get()->Next()->Twin();
      if(e != NULL && e->AdjFace() == f)
      {
         return true;
      }
   }
   return false;
}

int TriVertex::ComputeValence(void)
{
   valence=0;
   
   int val;
   
   CWVertexCirculator loop(this);
   for(loop.Start(), val=0; !loop.IsDone(); loop.Advance(), val++)
   {
#ifdef _DEBUG
      if(val==1000)
      {
         cerr << "Warning: touched 1000 edges while computing valence" << endl;
         break;
      }
#endif
   }
   
   valence=val;
   return valence;
}

#ifdef _DEBUG
bool TriVertex::VerifyTopology(int pos) const
{
   bool result=true;
   
   if(edge==NULL)
   {
      cerr << "TriVertex at position " << pos << " does not have edge" << endl;
      result=false;
   }
   return result;
}
#endif

void TriVertex::Draw(void) const
{
    glVertex3f(coords[0], coords[1], coords[2]);
}

void TriVertex::Draw(TriFaceRender *properties) const
{
   //Per vertex normals
   if(properties->vertexNormals)
   {
      if(properties->invertNormal)
      {
         glNormal3f(-normal[0], -normal[1], -normal[2]);
      }
      else
      {
         glNormal3f(normal[0], normal[1], normal[2]);
      }
   }
   
   //Per vertex color
#if 0
   GLfloat col[]={0.0, 0.0, 0.0, 1.0};
   if(boundary)
   {
      col[0]=1.0;
   }
   else
   {
      col[1]=1.0;
   }
   glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, col);
   glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, col);
   glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 30.0);
   glColor4fv(col);
#elif 0
   GLfloat col[]={0.0, 0.0, 0.0, 1.0};
   if(marked)
   {
      col[0]=1.0;
   }
   else
   {
      col[1]=1.0;
   }
   glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, col);
   glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, col);
   glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, 30.0);
   glColor4fv(col);
#elif 1
   if(properties->vertexColors)
   {
      //Compute linearly between the max and min values
      GLfloat val=((GLfloat)DisplayProperty()-properties->minVal)/(properties->maxVal-properties->minVal);
      GLfloat col[4];
#if 0 //Greyscale
      col[0]=val;
      col[1]=val;
      col[2]=val;

#else //Colors
      col[0]=val;
      col[1]=0.0;
      col[2]=1-val;
#endif
      col[3]=1.0;

      glMaterialfv(GL_FRONT_AND_BACK, GL_AMBIENT_AND_DIFFUSE, col);
      glMaterialfv(GL_FRONT_AND_BACK, GL_SPECULAR, col);
      glMaterialf(GL_FRONT_AND_BACK, GL_SHININESS, properties->shininess);
      glColor4fv(col);
   }
#endif      
   //Coordinates
   glVertex3f(Position()[0], Position()[1], Position()[2]);
}


Matrix33 TriVertex::GetLocalReferenceSystem(void)
{
    // This TriVertex is V. Its normal is n
    Vector3 n = this->Normal();

    // vP is neighbor TriVertex called P
    TriVertex *vP = this->edge->Head();

    // Ec. Plane is A�x + B�y + C�z + D = 0
    // A = nx ; B = ny; C = nz  �D?
    Real D = - (n[0]* this->coords[0] + n[1]* this->coords[1] + n[2]* this->coords[2]);

    // Calculate line which cross Plane => Normal with A�x + B�y + C�z + D = 0 and containt vP TriVertex
    Real t = (n[0]* vP->coords[0] + n[1]* vP->coords[1] + n[2]* vP->coords[2] + D) / -(n[0]*n[0] + n[1]*n[1] + n[2]*n[2]);

    // vQ is P projection over plane
    TriVertex vQ;
    vQ.SetNormal(n);
    vQ.Position(    vP->coords[0] + t* n[0], 
                    vP->coords[1] + t* n[1],
                    vP->coords[2] + t* n[2]);

    Vector3 VQ = vQ.Position() - this->Position();
    // The third vector is a cross product of n X VQ
    Vector3 nXvq = Vector3::crossProd (n, VQ);

    n.normalize();
    VQ.normalize();
    nXvq.normalize();

    //We have three vector: n, VQ and nXvq
    Matrix33 localRefSystem(n, VQ, nXvq);
    //std::cout << "Det=" <<localRefSystem.getDet() << std::endl;
    
    return localRefSystem;
}
vector<int>* TriVertex::GetIncidentFaces(void) const
{
	vector<int> *result = new vector<int>();
	for(CWVertexCirculator circ(this); !circ.IsDone(); circ.Advance())
	{
		result->push_back(circ.Get()->AdjFace()->Id());
	}

	sort(result->begin(), result->end());
	return result;
}

vector<TriFace*> TriVertex::GetFaces(void) const
{
   vector<TriFace*> result;
   for(CWVertexCirculator circ(this); !circ.IsDone(); circ.Advance())
   {
      result.push_back(circ.Get()->AdjFace());
   }
   return result;
}

